// AP(r) Computer Science Marine Biology Simulation:
// The UnboundedEnv class is copyright(c) 2002 College Entrance
// Examination Board (www.collegeboard.com).
//
// This class is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation.
//
// This class is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
import java.util.ArrayList;
/**
* AP® Computer Science Marine Biology Simulation:<br>
* The <code>UnboundedEnv</code> class models an unbounded, two-dimensional,
* grid-like environment containing locatable objects. For example, it
* could be an environment of fish for a marine biology simulation.
*
* <p>
* Modification History:
* - Created to support multiple environment representations:
* this class represents a second implementation of the
* <code>Environment</code> interface.
*
* <p>
* The <code>UnboundedEnv</code> class is
* copyright© 2002 College Entrance Examination Board
* (www.collegeboard.com).
*
* @author Alyce Brady
* @author APCS Development Committee
* @version 1 July 2002
* @see Locatable
* @see Location
**/
public class UnboundedEnv extends SquareEnvironment
{
// Instance Variables: Encapsulated data for each UnboundedEnv object
private ArrayList objectList; // list of Locatable objects in environment
// constructors
/** Constructs an empty UnboundedEnv object.
**/
public UnboundedEnv()
{
// Construct and initialize inherited attributes.
super();
objectList = new ArrayList();
}
// accessor methods
/** Returns number of rows in this environment.
* @return the number of rows, or -1 if the environment is unbounded
**/
public int numRows()
{
return -1;
}
/** Returns number of columns in this environment.
* @return the number of columns, or -1 if the environment is unbounded
**/
public int numCols()
{
return -1;
}
/** Verifies whether a location is valid in this environment.
* @param loc location to check
* @return <code>true</code> if <code>loc</code> is valid;
* <code>false</code> otherwise
**/
public boolean isValid(Location loc)
{
// All non-null locations are valid in an unbounded environment.
return loc != null;
}
/** Returns the number of objects in this environment.
* @return the number of objects
**/
public int numObjects()
{
return objectList.size();
}
/** Returns all the objects in this environment.
* @return an array of all the environment objects
**/
public Locatable[] allObjects()
{
Locatable[] objectArray = new Locatable[objectList.size()];
// Put all the environment objects in the list.
for ( int index = 0; index < objectList.size(); index++ )
{
objectArray[index] = (Locatable) objectList.get(index);
}
return objectArray;
}
/** Determines whether a specific location in this environment is
* empty.
* @param loc the location to test
* @return <code>true</code> if <code>loc</code> is a
* valid location in the context of this environment
* and is empty; <code>false</code> otherwise
**/
public boolean isEmpty(Location loc)
{
return (objectAt(loc) == null);
}
/** Returns the object at a specific location in this environment.
* @param loc the location in which to look
* @return the object at location <code>loc</code>;
* <code>null</code> if <code>loc</code> is empty
**/
public Locatable objectAt(Location loc)
{
int index = indexOf(loc);
if ( index == -1 )
return null;
return (Locatable) objectList.get(index);
}
/** Creates a single string representing all the objects in this
* environment (not necessarily in any particular order).
* @return a string indicating all the objects in this environment
**/
public String toString()
{
Locatable[] theObjects = allObjects();
String s = "Environment contains " + numObjects() + " objects: ";
for ( int index = 0; index < theObjects.length; index++ )
s += theObjects[index].toString() + " ";
return s;
}
// modifier methods
/** Adds a new object to this environment at the location it specifies.
* (Precondition: <code>obj.location()</code> is a valid empty location.)
* @param obj the new object to be added
* @throws IllegalArgumentException if the precondition is not met
**/
public void add(Locatable obj)
{
// Check precondition. Location should be empty.
Location loc = obj.location();
if ( ! isEmpty(loc) )
throw new IllegalArgumentException("Location " + loc +
" is not a valid empty location");
// Add object to the environment.
objectList.add(obj);
}
/** Removes the object from this environment.
* (Precondition: <code>obj</code> is in this environment.)
* @param obj the object to be removed
* @throws IllegalArgumentException if the precondition is not met
**/
public void remove(Locatable obj)
{
// Find the index of the object to remove.
int index = indexOf(obj.location());
if ( index == -1 )
throw new IllegalArgumentException("Cannot remove " +
obj + "; not there");
// Remove the object.
objectList.remove(index);
}
/** Updates this environment to reflect the fact that an object moved.
* (Precondition: <code>obj.location()</code> is a valid location
* and there is no other object there.
* Postcondition: <code>obj</code> is at the appropriate location
* (<code>obj.location()</code>), and either <code>oldLoc</code> is
* equal to <code>obj.location()</code> (there was no movement) or
* <code>oldLoc</code> is empty.)
* @param obj the object that moved
* @param oldLoc the previous location of <code>obj</code>
* @throws IllegalArgumentException if the precondition is not met
**/
public void recordMove(Locatable obj, Location oldLoc)
{
int objectsAtOldLoc = 0;
int objectsAtNewLoc = 0;
// Look through the list to find how many objects are at old
// and new locations.
Location newLoc = obj.location();
for ( int index = 0; index < objectList.size(); index++ )
{
Locatable thisObj = (Locatable) objectList.get(index);
if ( thisObj.location().equals(oldLoc) )
objectsAtOldLoc++;
if ( thisObj.location().equals(newLoc) )
objectsAtNewLoc++;
}
// There should be one object at newLoc. If oldLoc equals
// newLoc, there should be one at oldLoc; otherwise, there
// should be none.
if ( ! ( objectsAtNewLoc == 1 &&
( oldLoc.equals(newLoc) || objectsAtOldLoc == 0 ) ) )
{
throw new IllegalArgumentException("Precondition violation moving "
+ obj + " from " + oldLoc);
}
}
// internal helper method
/** Get the index of the object at the specified location.
* @param loc the location in which to look
* @return the index of the object at location <code>loc</code>
* if there is one; -1 otherwise
**/
protected int indexOf(Location loc)
{
// Look through the list to find the object at the given location.
for ( int index = 0; index < objectList.size(); index++ )
{
Locatable obj = (Locatable) objectList.get(index);
if ( obj.location().equals(loc) )
{
// Found the object -- return its index.
return index;
}
}
// No such object found.
return -1;
}
}